package com.ciaojian.client.util;

import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 类型转换工具类
 *
 * @author
 */
public class Conver {

    /**
     * 全角半角转换间隔
     * ASCII表中除空格外的可见字符与对应的全角字符的相对偏移
     */
    private static final int BC_CONVERT_STEP = 65248;

    /**
     * 半角!
     * ASCII表中可见字符从!开始，偏移位值为33(Decimal)
     */
    private static final char DBC_CHAR_START = 33;

    /**
     * 半角~
     * ASCII表中可见字符到~结束，偏移位值为126(Decimal)
     */
    private static final char DBC_CHAR_END = 126;

    /**
     * 全角！
     * 全角对应于ASCII表的可见字符从！开始，偏移值为65281
     */
    private static final char SBC_CHAR_START = 65281;

    /**
     * 全角～
     * 全角对应于ASCII表的可见字符到～结束，偏移值为65374
     */
    private static final char SBC_CHAR_END = 65374;

    /**
     * 全角空格 12288
     * 全角空格的值，它没有遵从与ASCII的相对偏移，必须单独处理
     */
    private static final char SBC_SPACE = 12288;

    /**
     * 半角空格
     * 半角空格的值，在ASCII中为32(Decimal)
     */
    private static final char DBC_SPACE = ' ';

    /**
     * 避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
     */
    private Conver() {
    }

    /**
     * 转换为字符串<br>
     * 如果给定的值为null，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static String toStr(final Object value, final String defaultValue) {
        if (null == value) {
            return defaultValue;
        }
        if (value instanceof String) {
            return (String) value;
        }
        return value.toString();
    }

    /**
     * 转换为int<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static Integer toInt(final Object value, final Integer defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Integer) {
            return (Integer) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为int<br>
     * toInt同名方法，默认值0
     *
     * @param value
     * @return
     */
    public static Integer toInt(final Object value) {
        return toInt(value, 0);
    }

    /**
     * 转换为long<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static Long toLong(final Object value, final Long defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Long) {
            return (Long) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return new BigDecimal(valueStr).longValue();
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为double<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static Double toDouble(final Object value, final Double defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Double) {
            return (Double) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return new BigDecimal(valueStr).doubleValue();
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为Float<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static Float toFloat(final Object value, final Float defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Float) {
            return (Float) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return Float.parseFloat(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为短整型<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value
     * @param defaultValue
     * @return
     */
    public static Short toShort(final Object value, final Short defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Short) {
            return (Short) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return Short.parseShort(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为高精度数
     *
     * @param value
     * @param defaultValue
     * @return
     */
    public static BigDecimal toBigDecimal(final Object value, final BigDecimal defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return new BigDecimal(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * 转换为boolean<br>
     * 如果给定的值为空，或者转换失败，返回默认值
     *
     * @param value        被转换的值
     * @param defaultValue 转换错误时的默认值
     * @return
     */
    public static Boolean toBool(final Object value, final Boolean defaultValue) {
        if (value == null) {
            return defaultValue;
        }
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        final String valueStr = value.toString();
        if (StringUtils.isBlank(valueStr)) {
            return defaultValue;
        }
        try {
            return Boolean.parseBoolean(valueStr);
        } catch (Exception e) {
            return defaultValue;
        }
    }

    /**
     * write a int to a byte array
     *
     * @param b
     * @param offset
     * @param v
     */
    public static void writeIntLong(final byte[] b, int offset, final long v) {
        b[offset++] = (byte) ((v >> 0) & 0xFF);
        b[offset++] = (byte) ((v >> 8) & 0xFF);
        b[offset++] = (byte) ((v >> 16) & 0xFF);
        b[offset] = (byte) ((v >> 24) & 0xFF);
    }

    /**
     * get a int from a byte array start from the specifiled offset
     *
     * @param b
     * @param offset
     */
    public static long getIntLong(final byte[] b, int offset) {
        return (((b[offset++] & 0x000000FFL)) | ((b[offset++] << 8) & 0x0000FF00L) | ((b[offset++] << 16) & 0x00FF0000L)
                | ((b[offset] << 24) & 0xFF000000L));
    }

    /**
     * double转字符串，解决数值较大出现科学计数的问题
     *
     * @param d
     * @param n
     * @return
     */
    public static String toString(final double d, final int n) {
        java.text.NumberFormat nf = java.text.NumberFormat.getInstance();
        nf.setMaximumFractionDigits(n);
        return nf.format(d).replaceAll(",", "");
    }

    /**
     * 半角转全角
     *
     * @param text 被转换的字符串
     * @return 全角字符串
     */
    public static String toSBC(final String text) {
        return toSBC(text, null);
    }

    /**
     * 半角转全角
     *
     * @param text          被转换的字符串
     * @param notConvertSet 不替换的字符集合
     * @return 全角字符串
     */
    public static String toSBC(final String text, final Set<Character> notConvertSet) {
        char c[] = text.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (null != notConvertSet && notConvertSet.contains(c[i])) {
                // 跳过不替换的字符
                continue;
            }
            // 如果是半角空格，直接用全角空格替代
            if (c[i] == DBC_SPACE) {
                c[i] = SBC_SPACE;
            }
            // 字符是!到~之间的可见字符
            else if ((c[i] >= DBC_CHAR_START) && (c[i] <= DBC_CHAR_END)) {
                c[i] = (char) (c[i] + BC_CONVERT_STEP);
            }
        }
        return new String(c);
    }

    /**
     * 半角转换全角
     *
     * @param src
     * @return
     */
    public static int toSBC(char src) {
        int r = src;
        // 如果是半角空格，直接用全角空格替代
        if (src == DBC_SPACE) {
            src = SBC_SPACE;
        }
        // 字符是!到~之间的可见字符
        else if ((src >= DBC_CHAR_START) && (src <= DBC_CHAR_END)) {
            r = src + BC_CONVERT_STEP;
        }
        return r;
    }

    /**
     * 全角转半角
     *
     * @param text 被转换的字符串
     * @return 半角字符串
     */
    public static String toDBC(final String text) {
        return toDBC(text, null);
    }

    /**
     * 替换全角为半角
     *
     * @param text          被转换的字符串
     * @param notConvertSet 不替换的字符集合
     * @return 替换后的字符
     */
    public static String toDBC(final String text, final Set<Character> notConvertSet) {
        char c[] = text.toCharArray();
        for (int i = 0; i < c.length; i++) {
            if (null != notConvertSet && notConvertSet.contains(c[i])) {
                // 跳过不替换的字符
                continue;
            }
            // 如果位于全角！到全角～区间内
            if (c[i] >= SBC_CHAR_START && c[i] <= SBC_CHAR_END) {
                c[i] = (char) (c[i] - BC_CONVERT_STEP);
            }
            // 如果是全角空格
            else if (c[i] == SBC_SPACE) {
                c[i] = DBC_SPACE;
            }
        }
        return new String(c);
    }

    /**
     * 全角转换半角
     *
     * @param src
     * @return
     */
    public static int toDBC(final char src) {
        int r = src;
        // 如果位于全角！到全角～区间内
        if (src >= SBC_CHAR_START && src <= SBC_CHAR_END) {
            r = src - BC_CONVERT_STEP;
        }
        // 如果是全角空格
        else if (src == SBC_SPACE) {
            r = DBC_SPACE;
        }
        return r;
    }

    /**
     * 提供（相对）精确的除法运算<br>
     * 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(final double v1, final double v2, final int scale) {
        if (scale < 0) {
            return 0;
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 四舍五入取小数
     *
     * @param d   被转换的双精度浮点小数
     * @param dot 小数点后位数
     * @return
     */
    public static Double scale(final Double d, final int dot) {
        if (d == null) {
            return null;
        }
        return BigDecimal.valueOf(d).setScale(dot, BigDecimal.ROUND_HALF_UP).doubleValue();
    }



    /**
     * 16进制哈希转字节数组
     *
     * @param s
     * @return
     */
    public static byte[] hexStrToBytes(final String s) {
        int i = s.length();
        byte result[] = new byte[i >> 1];
        for (int k = 0; k < i; ) {
            result[k >> 1] = (byte) (Integer.parseInt(s.substring(k, k += 2), 16));
        }
        return result;
    }



    /**
     * {@code null}检查<br>
     * 对象为{@code null}会抛NullPointerException
     *
     * @param reference
     * @return
     */
    public static <T> T checkNotNull(final T reference) {
        if (reference == null) {
            throw new NullPointerException();
        }
        return reference;
    }

    /**
     * 对象为{@code null}时取默认值
     *
     * @param reference
     * @param defaultVal
     * @return
     */
    public static <T> T notNull(final T reference, final T defaultVal) {
        return reference == null ? defaultVal : reference;
    }

    /**
     * 取不为空的int为null时返回0
     *
     * @param n 待处理的整形
     * @return
     */
    public static int notNull(final Integer n) {
        return notNull(n, 0);
    }

    /**
     * 取不为空的int为null时返回0
     *
     * @param n 待处理的整形
     * @return
     */
    public static long notNull(final Long n) {
        return notNull(n, 0L);
    }

    /**
     * 取不为空的浮点数<br>
     * 为null时返回0
     *
     * @param f 待处理的浮点数
     * @return
     */
    public static float notNull(final Float f) {
        return notNull(f, 0f);
    }

    /**
     * 取不为空的双精度浮点数<br>
     * 为null时返回0
     *
     * @param d 待处理的双精度浮点数
     * @return
     */
    public static double notNull(final Double d) {
        return notNull(d, 0d);
    }

    /**
     * 取不为空的list
     *
     * @param list
     * @return
     */
    public static <T> List<T> notNull(final List<T> list) {
        return list == null ? Collections.<T>emptyList() : list;
    }

    /**
     * 取不为空的set
     *
     * @param set
     * @return
     */
    public static <T> Set<T> notNull(final Set<T> set) {
        return set == null ? Collections.<T>emptySet() : set;
    }

    /**
     * 取不为空的map
     *
     * @param map
     * @return
     */
    public static <K, V> Map<K, V> notNull(final Map<K, V> map) {
        return map == null ? Collections.<K, V>emptyMap() : map;
    }

    /**
     * 取不为空的字符串
     *
     * @param s            待处理的字符串
     * @param defaultValue 为空时取默认值
     * @return
     */
    public static String notBlank(final String s, final String defaultValue) {
        return StringUtils.isBlank(s) ? defaultValue : s;
    }

    /**
     * 字符串转字符集
     *
     * @param encoding
     * @return
     */
    public static Charset toCharset(final String encoding) {
        if (StringUtils.isNotEmpty(encoding)) {
            return Charset.forName(encoding);
        }
        return StandardCharsets.UTF_8;
    }

    /**
     * 元转分
     *
     * @param yuan
     * @return
     */
    public static Long yuan2Fen(Double yuan) {
        return new BigDecimal(String.valueOf(yuan)).movePointRight(2).longValue();
    }

    /**
     * 分转元
     *
     * @param fen
     * @return
     */
    public static Double fen2Yuan(Long fen) {
        return new BigDecimal(fen).movePointLeft(2).doubleValue();
    }

}
